<?php namespace sacvoyage\storage;

use \sacvoyage\Object;


/*
 * 
 */
class Limiter implements \Iterator {
	
	public function __construct(\Iterator $iterator=null, $limit=null) {
		if($iterator) {
			$this->_iterator=$iterator;
		}
		$this->_limit=$this->_normalizeLimit($limit);
	}
	
	public function rewind() {
		$this->_iterator->rewind();
		if(!is_null($this->_limit->start)) {
			for($i=0; $i<($this->_limit->start) && $this->_iterator->valid(); $i++) {
				$this->_iterator->next();
			}
		}
		$this->_position=0;
	}

	public function key() {
		return $this->_position; // ??? return $this->_iterator->key();
	}

	public function current() {
		return $this->_iterator->current();
	}
	
	public function next() {
		$this->_iterator->next();
		$this->_position++;
	}

	public function valid() {
		if(!is_null($this->_limit->count)
		&& $this->_position>=$this->_limit->count) {
			return false;
		}
		return $this->_iterator->valid();
	}
	
	private $_iterator=null;
	private $_limit=null;
	private $_position=null; 
	
	
	
	/*
	 * limit can be: 10, [10], [10, 20], {"start": 10}
	 * 
	 * 
	 * 
	 */
	private function _normalizeLimit($limit) {
		
		// null is null
		if(is_null($limit)) {
			return new Object(array('start'=>null, 'count'=>null));
		}
		
		// just number
		elseif(is_numeric($limit)) {
			return $this->_normalizeLimit(
				new Object(array('count'=>$limit))
			);
		}
		
		// array of two values
		elseif (is_enumerative_array($limit) && count($limit)==2) {
			return $this->_normalizeLimit(
				new Object(array('start'=>$limit[0], 'count'=>$limit[1]))
			);
		} 

		// full limit object
		elseif(is_object($limit) 
		&&     $limit->hasAttr('start') && $limit->hasAttr('count')) {
			if(!is_null($limit->start)
			&& !(is_numeric($limit->start) && $limit->start>0)) {
				throw new InputException(
					'query.limit.start can be null or a positive number; '.
					gettype($limit).' given'
				);
			}
			
			if(!is_null($limit->count)
			&& !(is_numeric($limit->count) && $limit->count>0)) {
				throw new InputException(
					'query.limit.count can be null or a positive number; '.
					gettype($limit).' given'
				);
			}
			
			return $limit;			
		}
		
		// limit.start is absent set to null
		elseif(is_object($limit) && isset($limit->start)) {
			return $this->_normalizeLimit(
				new Object(array('start'=>$limit->start, 'count'=>null))
			);
		}
		
		// limit.count is absent set to null
		elseif(is_object($limit) && isset($limit->count)) {
			return $this->_normalizeLimit(
				new Object(array('start'=>null, 'count'=>$limit->count))
			);
		}
		
		elseif(is_object($limit)) {
			return new Object(array('start'=>null, 'count'=>null));
		}
		
		// error
		else {
			throw new InputException(
				'query.limit can be a number, an array, '.
				'an object or null; '.
				gettype($limit).' given'
			);
		}
		
	}
	
}

?>